#include "lib_MonitorCpu.h"
#include <iostream>
#include <unistd.h>
#include <cstring>

using namespace std;

MonitorCpu::MonitorCpu()
{
    long cpu_num = get_cpu_num();
    sysCpuTime_.resize(2);
    sysCpuTime_[0].cpu_num = cpu_num;
    sysCpuTime_[0].cpus.resize(cpu_num);
    sysCpuTime_[0].read_count = 0;
    sysCpuTime_[1].cpu_num = cpu_num;
    sysCpuTime_[1].cpus.resize(cpu_num);
    sysCpuTime_[1].read_count = 0;
    updae_cpu_time(false);
}

MonitorCpu::~MonitorCpu()
{

}

long MonitorCpu::get_cpu_num()
{
    return sysconf(_SC_NPROCESSORS_CONF);
}

sysCpuTime_t* MonitorCpu::updae_cpu_time(bool opt_cal_usage)
{
    sysCpuTime_t* prev, * curr;
    if (sysCpuTime_[0].read_count == sysCpuTime_[1].read_count)
    {
        curr = &sysCpuTime_[0];
        prev = &sysCpuTime_[1];
    }
    else if (sysCpuTime_[0].read_count > sysCpuTime_[1].read_count)
    {
        curr = &sysCpuTime_[1];
        prev = &sysCpuTime_[0];
    }
    else
    {
        cerr << "逻辑错误, 请检查代码逻辑" << endl;
        exit(-1);
    }

    FILE* fp = fopen(FILE_PROC_STAT_, "r");
    if (fp == NULL)
    {
        perror("fopen proc_stat failed");
        return NULL;
    }
    char line[256];
    int ret;
    long cpu_num = curr->cpu_num;
    cpuTime_t cpuTime_it;
    // 第一行读取cpu all
    if (fgets(line, sizeof(line), fp) != NULL)
    {
        ret = sscanf(line, "%*s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
            &cpuTime_it.user_time,
            &cpuTime_it.nice_time,
            &cpuTime_it.system_time,
            &cpuTime_it.idle_time,
            &cpuTime_it.iowait_time,
            &cpuTime_it.irq_time,
            &cpuTime_it.softirq_time,
            &cpuTime_it.steal_time,
            &cpuTime_it.guest_time,
            &cpuTime_it.guest_nice_time
        );
        if (ret > 0)
        {
            cpuTime_it.used_time = cpuTime_it.user_time + cpuTime_it.nice_time + cpuTime_it.system_time + cpuTime_it.irq_time
                + cpuTime_it.iowait_time + cpuTime_it.softirq_time + cpuTime_it.steal_time + cpuTime_it.guest_time + cpuTime_it.guest_nice_time;
            cpuTime_it.total_time = cpuTime_it.used_time + cpuTime_it.idle_time;
            curr->cpu_all = cpuTime_it;
        }
    }

    long i = 0;
    for (; i != cpu_num; i++)
    {
        if (fgets(line, sizeof(line), fp) == NULL) {
            break;
        }
        if (strstr(line, "cpu"))
        {
            ret = sscanf(line, "%*s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
                &cpuTime_it.user_time,
                &cpuTime_it.nice_time,
                &cpuTime_it.system_time,
                &cpuTime_it.idle_time,
                &cpuTime_it.iowait_time,
                &cpuTime_it.irq_time,
                &cpuTime_it.softirq_time,
                &cpuTime_it.steal_time,
                &cpuTime_it.guest_time,
                &cpuTime_it.guest_nice_time
            );
            if (ret > 0)
            {
                cpuTime_it.used_time = cpuTime_it.user_time + cpuTime_it.nice_time + cpuTime_it.system_time + cpuTime_it.irq_time
                    + cpuTime_it.iowait_time + cpuTime_it.softirq_time + cpuTime_it.steal_time + cpuTime_it.guest_time + cpuTime_it.guest_nice_time;
                cpuTime_it.total_time = cpuTime_it.used_time + cpuTime_it.idle_time;
                curr->cpus[i] = cpuTime_it;
            }
        }
    }
    fclose(fp);
    if (opt_cal_usage)
    {
        cal_cpu_usage(prev, curr);
    }
    curr_sysCpuTime_ = curr;

    if (i == cpu_num)
    {
        curr->read_count++;
        return curr;
    }

    return NULL;
}

int MonitorCpu::cal_cpu_usage(sysCpuTime_t* prev, sysCpuTime_t* curr)
{
    if (prev == nullptr || curr == nullptr)
    {
        return -1;
    }
    cpuTime_t* prev_cpuTime, * curr_cpuTime;
    // 计算总cpu使用率
    prev_cpuTime = &prev->cpu_all;
    curr_cpuTime = &curr->cpu_all;
    curr->cpu_all.usage = (curr_cpuTime->used_time - prev_cpuTime->used_time) / (curr_cpuTime->total_time - prev_cpuTime->total_time) * 100;
    curr->cpu_all.io_usage = (curr_cpuTime->iowait_time - prev_cpuTime->iowait_time) / (curr_cpuTime->total_time - prev_cpuTime->total_time) * 100;

    // 计算每个cpu使用率
    for (unsigned long i = 0; i != curr->cpu_num; i++)
    {
        prev_cpuTime = &prev->cpus[i];
        curr_cpuTime = &curr->cpus[i];
        curr->cpus[i].usage = (curr_cpuTime->used_time - prev_cpuTime->used_time) / (curr_cpuTime->total_time - prev_cpuTime->total_time) * 100;
        curr->cpu_all.io_usage = (curr_cpuTime->iowait_time - prev_cpuTime->iowait_time) / (curr_cpuTime->total_time - prev_cpuTime->total_time) * 100;
    }

    return 0;
}

void MonitorCpu::show_cpu_usage(sysCpuTime_t* curr_sysCpuTime)
{
    cpuTime_t* cpuTime_now = &curr_sysCpuTime->cpu_all;
    printf("cpu all: %3.2lf%%\n", cpuTime_now->usage);
    for (unsigned long i = 0; i < curr_sysCpuTime->cpu_num; i++)
    {
        cpuTime_now = &curr_sysCpuTime->cpus[i];
        printf("cpu[%2ld]: %3.2lf%% io:%5.2f\n", i, cpuTime_now->usage, cpuTime_now->io_usage);
    }
    printf("----------------\n");
}

unsigned long MonitorCpu::get_cpu_time(uint32_t cpu_id)
{
    // 总cpu time
    if (cpu_id == 0xfffffff)
    {
        return curr_sysCpuTime_->cpu_all.total_time;
    }

    return curr_sysCpuTime_->cpus[cpu_id].total_time;
}